namespace ChumChase.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Windows;
    using Caliburn.MVP.Presenters;

    public static class MvpConfiguration
    {
        /// <summary>
        /// Creates a set of data templates pair to presenter classes based on naming conventions.
        /// </summary>
        /// <remarks>If a data template is already present in the resources, then it is skipped.</remarks>
        /// <param name="resources">The ResourceDictionary that the data templates for the views will be added to.</param>
        public static void InferViewsFromPresenters(ResourceDictionary resources)
        {
            Dictionary<Type, Type> lookup = MatchPresentersToViews();

            foreach (var pair in lookup)
            {
                DataTemplate template = CreateDataTemplate(pair.Value, pair.Key);
                if (resources.Contains(template.DataTemplateKey)) continue;
                resources.Add(template.DataTemplateKey, template);
            }
        }

        private static Dictionary<Type, Type> MatchPresentersToViews()
        {
            IEnumerable<Type> presenters = from type in Assembly.GetExecutingAssembly().GetTypes()
                                           where typeof (IPresenter).IsAssignableFrom(type)
                                                 && !type.IsInterface
                                                 && !type.IsAbstract
                                           select type;

            Type first = presenters.FirstOrDefault();
            if (first == null) throw new Exception("I expected to find some presenters");

            string viewNamespace = first.Namespace
                .Replace(".Presenters", ".Views");
            viewNamespace = viewNamespace.Substring(0, viewNamespace.IndexOf(".Views") + 6);

            IEnumerable<Type> views = from view in Assembly.GetExecutingAssembly().GetTypes()
                                      where !string.IsNullOrEmpty(view.Namespace)
                                            && view.Namespace.StartsWith(viewNamespace)
                                      select view;

            var table = new Dictionary<Type, Type>();

            foreach (Type view in views)
            {
                Type presenter = GetPresenterForView(view, presenters);
                if (presenter == null) continue;

                table.Add(presenter, view);
            }

            return table;
        }

        private static Type GetPresenterForView(Type type, IEnumerable<Type> presenters)
        {
            string name = type.Name.Replace("View", "Presenter");
            Type presenter =
                presenters.Where(p => p.Name == name).FirstOrDefault();
            return presenter;
        }

        private static DataTemplate CreateDataTemplate(Type viewType, Type dataType)
        {
            var template = new DataTemplate(dataType);

            var factory = new FrameworkElementFactory(viewType);
            template.VisualTree = factory;
            return template;
        }
    }
}